package com.bnq.logc.ui.file

import android.Manifest
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bnq.logc.Logc
import com.bnq.logc.LogcManager
import com.bnq.logc.R
import com.bnq.logc.native.NativeLogc
import com.bnq.logc.utils.AlertDialogUtil
import com.bnq.logc.utils.BluetoothUtil
import com.bnq.logc.utils.DateUtil
import com.bnq.logc.utils.FileHelper
import com.bnq.logc.utils.FileUtil
import com.cj.clog.Clog
import java.io.File
import kotlin.concurrent.thread

class FileBrowserFragment : Fragment() {

    private val CHOOSE_FILE_REQUEST_CODE = 0x07
    private val PERMISSION_CODE_WRITE_EXTERNAL = 0x19


    /**
     * push 入栈
     * pop 出栈
     * peek 查看栈顶元素不出栈
     */
    private var stack:java.util.ArrayDeque<FileFilter> = java.util.ArrayDeque()
    private var adapter:Adapter? = null
    private var tvTitle:TextView? = null

    private var onBack:(()->Unit)? = null

    private var isEncodings = false

    private var permissionSuccessFun:(()->Unit)? = null

    private var onViewFileFun:((file:File)->Unit)? = null

    fun setOnBack(onBack:()->Unit){
        this.onBack = onBack
    }

    fun setOnViewFileCallback(onViewFileFun:((file:File)->Unit)){
        this.onViewFileFun = onViewFileFun
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_file_browser,container,false)
    }

    private fun showToast(msg:String){
        activity?.runOnUiThread {
            Toast.makeText(requireContext(),msg,Toast.LENGTH_SHORT).show()
        }
    }

    private fun runUI(callback:()->Unit){
        activity?.runOnUiThread {
            callback.invoke()
        }

    }

    private fun onRefresh(){
        activity?.runOnUiThread {
            val fileFilter = stack.peek()
            notifyFile(fileFilter)
        }
    }

    private fun requestWritePermission(invoke:()->Unit){
        /**
         * android 6.0以上动态权限申请：写权限
         */
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(requireActivity(),
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionSuccessFun = invoke
            requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_CODE_WRITE_EXTERNAL);
        } else {
            invoke.invoke()
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if(requestCode == PERMISSION_CODE_WRITE_EXTERNAL){
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                permissionSuccessFun?.invoke()
                permissionSuccessFun = null
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initView(view)
        val fileFilter = getFileFilter()

        val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.layoutManager = LinearLayoutManager(context)
        recyclerView.addItemDecoration(DividerItemDecoration(context,LinearLayoutManager.VERTICAL).apply { setDrawable(ColorDrawable(
            Color.LTGRAY)) })
        recyclerView.adapter = Adapter().apply {
            adapter = this
            setOnItemClickListener(object : Adapter.OnItemClickListener{
                override fun onItemClick(position: Int, file: File) {
                    if(file.isDirectory){
                        val fileFilter = FileFilter(file.absolutePath,"",FileFilter.Companion.FileType.ALL)
                        stack.push(fileFilter)
                        notifyFile(fileFilter)
                    }else{
                        //FileUtil.openTextFile(requireContext(),file.absolutePath)
                        onViewFileFun?.invoke(file)
                    }
                }
            })
            setOnItemMoreClickListener(object : Adapter.OnItemMoreClickListener{
                override fun onItemMoreClick(position: Int, file: File) {
                    AlertDialogUtil.showCommonListDialog(requireContext(),getFileMoreMakeMenuList(file).toTypedArray())
                }
            })
            stack.push(fileFilter)
            notifyFile(fileFilter)
        }
    }

    private fun copyFileToInner(src: Uri,out:String){
        thread {
            val outPath = out + "/logc/tmp/${DateUtil.getCurrentYMString(System.currentTimeMillis())}/${DateUtil.getCurrentYMDString(System.currentTimeMillis())}"
            FileHelper.copyFileFromUriToDataDir(requireActivity(),src,outPath)
            //跳转到
            val fileFilter = FileFilter(outPath,"",FileFilter.Companion.FileType.ALL)
            stack.push(fileFilter)
            notifyFile(fileFilter)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if(requestCode == CHOOSE_FILE_REQUEST_CODE && resultCode == RESULT_OK && data != null) {
            val uri = data.data
            val out = activity?.externalCacheDir?.absolutePath
            if(out != null && uri != null){
                copyFileToInner(uri,out)
            }else{
                showToast("源目的地src={$uri},或目的地dest={$out}不存在")
            }
        }

    }

    private fun openFileManagerChooseFile(){
        val intent = Intent(Intent.ACTION_GET_CONTENT)
        intent.type = "*/*"
        intent.addCategory(Intent.CATEGORY_OPENABLE)
        startActivityForResult(intent,CHOOSE_FILE_REQUEST_CODE)
    }

    private fun decryptCiphertextFile(file: File,privateKey:String? = null){
        if(isEncodings){
            showToast("正在解密中，稍后重试")
            return
        }
        isEncodings = true
        thread {
            val fileHead = Clog.getFileHeadOnly(file.absolutePath)
            if(fileHead != null){
                val serverPrivateKey = privateKey?:LogcManager.getConfig().mLogWriteModeConfig.serverPrivateKey
                if(!serverPrivateKey.isNullOrEmpty()){
                    val outPath = (file.parent?:"") + "/" + FileUtil.generateFileNameByOriginFileName(file.name)
                    File(outPath).apply {
                        if(exists()) {
                            delete()
                            onRefresh()
                        }
                    }
                    val ret = Clog.decryptCiphertextFile(file.absolutePath,outPath,serverPrivateKey)
                    if(ret){
                        showToast("解密成功")
                    }else{
                        showToast("解密失败")
                    }
                    onRefresh()
                }else{
                    showToast("当前日志记录是非加密的，请先设置私钥")
                }
            }else{
                showToast("文件头部不符合解密规则")
            }
            isEncodings = false
        }

    }

    private fun getFileMoreMakeMenuList(file: File):List<AlertDialogUtil.Companion.CommonItem>{
        val list = mutableListOf<AlertDialogUtil.Companion.CommonItem>()
        list.add(AlertDialogUtil.Companion.CommonItem(0,"解密文件"){dialog,item->
            dialog.dismiss()
            decryptCiphertextFile(file)
        })
        list.add(AlertDialogUtil.Companion.CommonItem(0,"解密文件(指定密钥)"){dialog,item->
            dialog.dismiss()
            AlertDialogUtil.showEditDialog(requireContext(),"解密文件密钥","请输入解密文件密钥","",confirm = {dialog,content->
                dialog.dismiss()
                if(content.isEmpty()){
                    showToast("密钥不能为空")
                }else{
                    decryptCiphertextFile(file,content)
                }
            })
        })
        list.add(AlertDialogUtil.Companion.CommonItem(1,"上传远程服务器"){dialog,item->
            dialog.dismiss()
            //println("----------file.absolutePath:${file.absolutePath},file.name:${file.name},file.parentFile:${file.parentFile},file.parent:${file.parent},path:${file.path}")
            val parentPath = LogcManager.getConfig().getParentFileDir().absolutePath
            if(FileUtil.isOriginFilePath(parentPath,file.absolutePath)){
                //本地记录的日志
                if(FileUtil.checkFileOriginal(file.name)){
                    //上传原始文件
                    Logc.syncLogFileNow(listOf(file)){
                        showToast(it.getDetailUploadResultInfoString())
                        runUI {
                            AlertDialogUtil.showCommonDialog(requireContext(),"提示","${it.getFormatUploadResultString()}")
                        }
                    }
                }else{
                    //上传非原始文件
                    val cloudPathName = "${LogcManager.getConfig().getCloudParentDir(file,
                        parseFileCreateTime = false,
                        parsePreParentPathName = true
                    )}/${file.name}"
                    Logc.syncTmpFileNow(listOf(file), listOf(cloudPathName)){
                        showToast(it.getDetailUploadResultInfoString())
                        runUI {
                            AlertDialogUtil.showCommonDialog(
                                requireContext(),
                                "提示",
                                "${it.getFormatUploadResultString()}"
                            )
                        }
                    }
                }
            }else{
                //外部拷贝的日志
                val cloudPathName = "${LogcManager.getConfig().getExternalFileLogParentDir(file)}/${file.name}"
                Logc.syncTmpFileNow(listOf(file), listOf(cloudPathName)){
                    showToast(it.getDetailUploadResultInfoString())
                    runUI {
                        AlertDialogUtil.showCommonDialog(
                            requireContext(),
                            "提示",
                            "${it.getFormatUploadResultString()}"
                        )
                    }
                }
            }
        })
        list.add(AlertDialogUtil.Companion.CommonItem(2,"拷贝到Download"){dialog,item->
            dialog.dismiss()
            requestWritePermission{
                thread {
                    val outPath = FileHelper.getExternalDownloadsUri(requireActivity(),FileUtil.generateFileNameByOriginFileName(file.name,".copy"))
                    val ret = FileHelper.copySandFileToExternalUri(requireActivity(),file.absolutePath,outPath)
                    if(ret){
                        showToast("拷贝成功")
                    }else{
                        showToast("拷贝失败")
                    }
                }
            }
        })
        list.add(AlertDialogUtil.Companion.CommonItem(3,"复制文件"){dialog,item->
            dialog.dismiss()
            thread {
                val ret = FileHelper.copyFile(file,File(file.parent + "/" + FileUtil.generateFileNameByOriginFileName(file.name,".copy")))
                if(ret){
                    showToast("拷贝成功")
                }else{
                    showToast("拷贝失败")
                }
                onRefresh()
            }
        })
        list.add(AlertDialogUtil.Companion.CommonItem(4,"删除文件"){dialog,item->
            AlertDialogUtil.showCommonDialog(requireContext(),"提示","确定删除文件吗？", cancel = {
                it.dismiss()
            },confirm = {
                it.dismiss()
                dialog.dismiss()
                if(file.exists()){
                    if(FileUtil.checkFileOriginal(file.name)){
                        showToast("原始日志文件无法删除")
                        return@showCommonDialog
                    }
                    if(file.delete()){
                        showToast("删除成功")
                        onRefresh()
                    }else{
                        showToast("删除失败")
                    }
                }else{
                    showToast("文件不存在")
                }
            })
        })
        list.add(AlertDialogUtil.Companion.CommonItem(5,"修改文件名"){dialog,item->
            dialog.dismiss()
            if(FileUtil.checkFileOriginal(file.name)){
                showToast("原始日志文件无法修改文件名")
                return@CommonItem
            }
            AlertDialogUtil.showEditDialog(requireContext(),"修改文件名","请输入文件名",file.name,confirm = {dialog,content->
                dialog.dismiss()
                val newName = content
                if(FileUtil.checkFileOriginal(newName)/* && FileUtil.isHasOriginFileForParentDir(file.parentFile)*/){
                    //showToast("当前文件夹下已有原始文件，不允许修改，请重新输入其他名字")
                    showToast("无法修改成原始日志文件名，请重新输入其他名字")
                    return@showEditDialog
                }
                if(newName.isNullOrEmpty()){
                    showToast("文件名不能为空")
                    return@showEditDialog
                }
                val newFile = File(file.parent + "/" + newName)
                if(file.renameTo(newFile)){
                    showToast("修改成功")
                    onRefresh()
                }else{
                    showToast("修改失败")
                }
            })
        })
        list.add(AlertDialogUtil.Companion.CommonItem(6,"查看文件"){dialog,item->
            dialog.dismiss()
            FileUtil.openTextFile(requireContext(),file.absolutePath)
            //onViewFileFun?.invoke(file)
        })
        list.add(AlertDialogUtil.Companion.CommonItem(7,"分享文件"){dialog,item->
            dialog.dismiss()
            BluetoothUtil.bluetoothSendFile(this@FileBrowserFragment,file,0x123)
        })
        return list
    }

    private fun notifyFile(fileFilter: FileFilter){
        requireActivity().runOnUiThread {
            tvTitle?.text = fileFilter.getName()
            File(fileFilter.parentPath).listFiles()?.let { adapter?.notify(it.toList()) }
        }
    }

    private fun initView(view: View){
        view.findViewById<ImageView>(R.id.back).apply {
            setOnClickListener{
                if(stack.size > 1){
                    stack.pop()
                    val fileFilter = stack.peek()
                    notifyFile(fileFilter)
                }else{
                    onBack?.invoke()
                }
            }
        }
        tvTitle = view.findViewById<TextView>(R.id.title)
        view.findViewById<View>(R.id.choose_file).setOnClickListener {
            openFileManagerChooseFile()
        }
        view.findViewById<View>(R.id.open_external_file).setOnClickListener {
            requireActivity().externalCacheDir?.absolutePath?.let {
                val fileFilter = FileFilter("$it/logc/tmp", "", FileFilter.Companion.FileType.ALL)
                stack.push(fileFilter)
                notifyFile(fileFilter)
            }
        }
    }

    private fun getFileFilter():FileFilter{
        val _parentPath = LogcManager.getConfig().getParentFileDir().absolutePath
        return arguments?.let {
            val parentPath = it.getString("parentPath")?: _parentPath
            val search = it.getString("search")?:""
            val fileType = (it.getSerializable("fileType") as FileFilter.Companion.FileType?) ?: FileFilter.Companion.FileType.ALL
            val fileFilter = FileFilter(parentPath,search,fileType)
            fileFilter
        }?:FileFilter(_parentPath,"",FileFilter.Companion.FileType.ALL)
    }

    class Adapter : RecyclerView.Adapter<Adapter.VH>(){

        private var files = mutableListOf<File>()

        open class VH(view: View) : RecyclerView.ViewHolder(view)

        class File_VH(view: View) : VH(view){
            private val imageView:ImageView = view.findViewById(R.id.image)
            private val tvName:TextView = view.findViewById(R.id.title)
            private val tvSubtitle:TextView = view.findViewById(R.id.subtitle)
            val btnMore:View = view.findViewById(R.id.btn_more)

            private fun getFileTimeStamp(file: File):String{
                var timestamp = NativeLogc.getFileCreateTimeStamp(file)
                if(timestamp == 0L) timestamp = file.lastModified()
                return DateUtil.formatDateTime(timestamp)
            }

            fun bind(file: File){
                tvName.text = file.name
                tvSubtitle.text = "${getFileTimeStamp(file)}   ${FileUtil.formatFileSize(file.length())}"
            }

        }

        class Dir_VH(view: View) : VH(view){
            private val imageView:ImageView = view.findViewById(R.id.image)
            private val tvName:TextView = view.findViewById(R.id.title)

            fun bind(file: File){
                tvName.text = file.name
            }
        }

        override fun getItemViewType(position: Int): Int {
            return if(files[position].isFile){
                1
            }else{
                0
            }
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
            return if(viewType == 1){
                File_VH(LayoutInflater.from(parent.context).inflate(R.layout.item_file_browser_file,parent,false))
            }else{
                Dir_VH(LayoutInflater.from(parent.context).inflate(R.layout.item_file_browser_dir,parent,false))
            }
        }

        override fun getItemCount(): Int = files.size

        override fun onBindViewHolder(holder: VH, position: Int) {
            when(holder){
                is File_VH -> {
                    val file = files[position]
                    holder.bind(file)
                    holder.itemView.setOnClickListener {
                        onItemClickListener?.onItemClick(position,file)
                    }
                    holder.btnMore.setOnClickListener {
                        onItemMoreClickListener?.onItemMoreClick(position,file)
                    }
                }
                is Dir_VH -> {
                    val file = files[position]
                    holder.bind(file)
                    holder.itemView.setOnClickListener {
                        onItemClickListener?.onItemClick(position,file)
                    }
                }
            }
        }

        fun notify(files:List<File>){
            this.files.clear()
            this.files.addAll(files)
            notifyDataSetChanged()
        }

        public interface OnItemClickListener{
            fun onItemClick(position: Int,file:File)
        }

        private var onItemClickListener:OnItemClickListener? = null

        fun setOnItemClickListener(onItemClickListener: OnItemClickListener){
            this.onItemClickListener = onItemClickListener
        }

        public interface OnItemMoreClickListener{
            fun onItemMoreClick(position: Int,file:File)
        }

        private var onItemMoreClickListener:OnItemMoreClickListener? = null

        fun setOnItemMoreClickListener(onItemMoreClickListener: OnItemMoreClickListener){
            this.onItemMoreClickListener = onItemMoreClickListener
        }


    }

}